#coding: utf-8

import Operators
import Register
import AsmPanic
import Instruction

'''
    Classe que irá ser o responsavel por ler o fonte ASM
    e interpretar ele.
    
    @author: iuri.andreazza
'''
class AsmParser:
    __fl = None;                            #arquivo ASM
    __parsedInstr = {};                     #Lista de instruções lidas e validadas
    __typeAdd = ["ADDU","ADD", "ADDF"];     #Lista de ADDs possiveis
    __typeMul = ["MUL", "MULO", "MULOU"];   #Lista de MULs possiveis
    __typeSub = ["SUB", "SUBU"];            #Lista de SUBs possiveis
    __typeOr = ["OR", "XOR", "NOR"];        #Lista de ORs possiveis
    __type_R = ["AND", "NOT"];              #Lista de AND ou NOT
    __realDep = {}                          #Lista de dependências reais
    __outputDep = {}                        #Lista de dependências de saída
    __antiDep = {}                          #Lista de antidependềncias

    def __init__(self):
        self.__parsedInstr = {};
        self.__fl = None;

    def openASMFile(self, fileName):
        self.__fl = open(fileName);
        
    def getParsedInstr(self):
        return self.__parsedInstr
    
    '''
        Procura antidependência e dependências reais e de saída
    '''
    def __checkDependencies(self, regOptorIn = {}, regOptingIn = {}):
        '''
        regOptorIn é o dicionário que guarda registradores operadores
        regOptingIn é o dicionário que guarda registradores operandos
        
        Ambos são estruturas no formato registrador: [linhas onde ele aparece]
        '''
        #dependência de saída é quando os registradores são escritos em mais de
        #  uma instrução
        for key in regOptorIn.keys():
            if regOptorIn[key].__len__() > 1:
                self.__outputDep[key] = regOptorIn[key]
#        print "output", self.__outputDep

        #dependência real são os registradores de mesma chave e com mesmos valores
        #  de dentro do array, onde um operando é escrito pouco antes de ser
        #  manipulado
        #antidependência são os registradores de mesma chave e com os mesmos valores
        #  onde um registrador vai ser reescrito enquanto está sendo manipulado
        #  por uma instrução anterior
        
        # varre as estruturas de operadores e operandos procurando dependências 
        for regOr in regOptorIn:
            for regIng in regOptingIn:
                # se o operando for igual ao operador
                if regOr == regIng:
                    # varre as linhas onde esses registradores se encontram
                    for linO in regOptorIn[regOr]:          #operador
                        for linI in regOptingIn[regIng]:    #operando
                            # dependência real
                            if linO < linI:
                                #se for nova instrução, cria um novo array
                                if not self.__realDep.has_key(regOr):
                                    self.__realDep[regOr] = []
                                #evita inserir chaves duplicadas
                                if not self.__realDep[regOr].__contains__(linO):
                                    self.__realDep[regOr].append(linO)
                                if not self.__realDep[regOr].__contains__(linI):
                                    self.__realDep[regOr].append(linI)
                            # anti-depenência
                            else:
                                if not self.__antiDep.has_key(regOr):
                                    self.__antiDep[regOr] = []
                                if not self.__antiDep[regOr].__contains__(linO):
                                    self.__antiDep[regOr].append(linO)
                                if not self.__antiDep[regOr].__contains__(linI):
                                    self.__antiDep[regOr].append(linI)
#        print "real", self.__realDep
#        print "anti", self.__antiDep
        
    
    def __tryParse(self):
        ln = 0;
        regOptorIn = {}                       #Lista de operadores
        regOptingIn = {}                      #Lista de operandos
        #para cada linha do arquivo ASM
        for lin in self.__fl:
            label = ln; #define o label da instrução
            #prepara a linha para processamento    
            lin = lin.upper();  #Normalizando os caracteres
            lin = lin.strip();  #removendo os espaços em branco desnessesários
            lin = lin.split();  #quebrando a linha para ser lida
            
            comentario = "";
            op = lin.pop(0);    #operador
            lastIt = lin.pop(); #ultimo dado (pode ser um comentário)
            
            if(lastIt.strip().startswith(";")):
                comentario = lastIt;
            else:
                lin.append(lastIt); #volta ele para a lista
            
            #Cria dicionários com as linhas onde é usado cada registrador,
            #  com um dicionário para operandos e outro para operadores
            for i in range(3):
                if lin[i][lin[i].__len__() - 1] == ',':
                    lin[i] = lin[i][:lin[i].__len__() - 1];
                if i == 0:
                    if not regOptorIn.has_key(lin[i]):
                        regOptorIn[lin[i]] = [];
                    regOptorIn[lin[i]].append(label);
                else:
                    if not regOptingIn.has_key(lin[i]):
                        regOptingIn[lin[i]] = [];
                    regOptingIn[lin[i]].append(label);
                               
            params = self.__parseParams(lin);
            
            #Cria a instrução do tipo adequado
            if op == "LW":
                self.__parsedInstr[label] = Instruction.Instruction(label, Operators.LW(params, comentario), 'lw');
            elif op == "SW":
                self.__parsedInstr[label] = Instruction.Instruction(label, Operators.SW(params, comentario), 'sw');
            elif op in self.__typeAdd:
                for i in range(len(params)):
                    if(i == 0): params[i].setWriting();
                    else: params[i].setReading();
                self.__parsedInstr[label] = Instruction.Instruction(label, Operators.ADD(params, comentario, op));
            elif op in self.__typeMul:
                for i in range(len(params)):
                    if(i == 0): params[i].setWriting();
                    else: params[i].setReading();
                self.__parsedInstr[label] = Instruction.Instruction(label, Operators.MUL(params, comentario, op));
            elif op in self.__typeSub:
                for i in range(len(params)):
                    if(i == 0): params[i].setWriting(); 
                    else: params[i].setReading();
                self.__parsedInstr[label] = Instruction.Instruction(label, Operators.SUB(params, comentario, op));
            elif op in self.__typeOr:
                for i in range(len(params)):
                    if(i == 0): params[i].setWriting();
                    else: params[i].setReading();
                self.__parsedInstr[label] = Instruction.Instruction(label, Operators.OR(params, comentario, op));
            elif op in self.__type_R:
                for i in range(len(params)):
                    if(i == 0): params[i].setWriting();
                    else: params[i].setReading();
                if op == self.__type_R[0]:
                    self.__parsedInstr[label] = Instruction.Instruction(label, Operators.AND(params, comentario));
                elif op == self.__type_R[1]:
                    self.__parsedInstr[label] = Instruction.Instruction(label, Operators.NOT(params, comentario));
            else:
                raise AsmPanic.AsmPanic("UserError: This code is WRONG! Fix it and then come back");
                
            ln = ln + 1;
        self.__checkDependencies(regOptorIn, regOptingIn);
        return 1;
    
    def __parseParams(self, params):
        paramLst = [];
        for param in params:
                regNum = 0;
                param  = param.strip();
                if(param[0] == "$"):
                    try:
                        #limpa o nome do Registrador para ver seu numero
                        regParsed = param.replace("$", "");
                        regParsed = regParsed.replace("T", "");
                        regParsed = regParsed.replace("t", "");
                        regParsed = regParsed.replace(",", "");
                        regNum = int(regParsed);
                        paramLst.append(Register.Register(param[1], regNum));
                    except ValueError:
                        raise AsmPanic.AsmPanic("Linha: "+self.__instr[self.__pp].getLabel()+" - Valor Incorreto para o Resgistrador");
        return paramLst;
    
    '''
        Faz o parse do arquivo ASM
        
        @raise AsmPanic: Ocorreu algum erro inesperado que o parser não conseguiu evitar.
    '''
    def doParse(self):
        if(self.__tryParse() <= 0):
            raise AsmPanic.AsmPanic("AHHHHH, ocorreu um erro inesperado durante o parser!!")
        else:
            return self.__parsedInstr;